package com.apobates.forum.grief.concurrent;

import com.apobates.forum.grief.Commons;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 值对象的集合异步组合模板
 * @param <V> 值对象的类型
 * @param <R> 目标对象的类型
 */
public class VoCombineCollectionAsyncTemplate<V,R> implements Serializable {
    //值对象的类
    private final Class<V> vClass;
    private final List<R> targetList;
    private CombineResultStream<V> targetStream;

    /**
     * 设置值对象,例: TopicVo
     * @param tClass 值对象的Class, 例: TopicVo.class
     * @param <V> 值对象的类型
     * @param <R> 目标对象的类型
     * @return
     */
    public static <V,R> VoCombineCollectionAsyncTemplate<V,R> template(Class<V> tClass) throws IllegalStateException {
        return new VoCombineCollectionAsyncTemplate(tClass);
    }

    private VoCombineCollectionAsyncTemplate(Class<V> tClass) throws IllegalStateException {
        this.vClass = tClass;
        this.targetList = Collections.emptyList();
        //
        this.targetStream = null;
    }

    private VoCombineCollectionAsyncTemplate(Class<V> vClass, String targetAttrName, List<R> targetList) {
        this.vClass = vClass;
        this.targetList = targetList;
        //
        if(!targetList.isEmpty()){
            this.targetStream = new CombineResultStream<>(vClass, targetAttrName, targetList);
        }
    }

    private VoCombineCollectionAsyncTemplate(CombineResultStream<V> targetStream, Class<V> vClass, List<R> targetList) {
        this.vClass = vClass;
        this.targetList = targetList;
        //
        this.targetStream = targetStream;
    }

    /**
     * 设置目标实例Stream,例: Topic;
     * @param targetAttrName 目标实例在值对象的属性名
     * @param source 目标实例流
     * @return
     **/
    public VoCombineCollectionAsyncTemplate stream(final String targetAttrName, List<R> source){
        return new VoCombineCollectionAsyncTemplate(this.vClass, targetAttrName, source);
    }

    /**
     * 设置组合对象
     * @param promise 组合对象的契约接口
     * @param <ID> 组合对象查询参数的类型
     * @param <T> 组合对象的类型
     * @return
     */
    public <ID,T> VoCombineCollectionAsyncTemplate combine(CombineCollectionPromise<ID,T, V, R> promise){
        Set<ID> idSet = targetList.stream().map(robj->promise.param(robj)).collect(Collectors.toSet());
        if(idSet.isEmpty()){
            return this;
        }
        if(null == this.targetStream){
            return this;
        }
        CompletableFuture<Stream<T>> data = promise.query(idSet);
        CombineResultStream<V> _crs = this.targetStream.query(promise.mapper(), data);
        return new VoCombineCollectionAsyncTemplate(_crs, this.vClass, this.targetList);
    }

    /**
     * 返回最终的值对象
     * @return
     */
    public List<V> toList(){
        if(null != this.targetStream){
            return this.targetStream.getResult();
        }
        return Collections.emptyList();
    }

    //获得ID-Map
    private class CombineResultStream<V> {
        private CompletableFuture<Stream<V>> rawdata;;

        public CombineResultStream(final Class<V> vClass, final String attributeName, List<R> targetList){
            Stream<V> rs = targetList.stream().map(robj->{
                V tmp = Commons.getInstance(vClass).orElse(null);
                if(null != tmp) {
                    setAttrVal(tmp, attributeName, robj);
                }
                return tmp;
            }).filter(Objects::nonNull);
            this.rawdata = CompletableFuture.supplyAsync(()->rs);
        }

        private CombineResultStream(CompletableFuture<Stream<V>> instream){
            this.rawdata = instream;
        }

        public <T> CombineResultStream query(BiFunction<Stream<V>, Stream<T>, Stream<V>> mapper, CompletableFuture<Stream<T>> data){
            CompletableFuture<Stream<V>> tmp = data.thenCompose(cbStms->{
                return this.rawdata.thenApply(tarStms->{
                    return mapper.apply(tarStms, cbStms);
                });
            }).completeOnTimeout(Stream.empty(), 1, TimeUnit.SECONDS);
            return new CombineResultStream(tmp);
        }

        public List<V> getResult(){
            return this.rawdata.completeOnTimeout(Stream.empty(), 1, TimeUnit.SECONDS).join().collect(Collectors.toList());
        }
    }

    private static String getAttrName(Class<?> cls){
        return cls.getSimpleName().toLowerCase();
    }

    private static void setAttrVal(Object obj, String attrName, Object attrVal){
        try{
            Commons.setAttrValByReflectField(obj, attrName, attrVal);
        }catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e){
            e.printStackTrace();
        }
    }
}
